/**
 *  Copyright 2010 Kyle O'Connor, Justin Arling, Mike Mercieca
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package edu.drexel.sd0910.ece01.aqmon.gui.wizard;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JSeparator;

import org.apache.log4j.Logger;

import edu.drexel.sd0910.ece01.aqmon.gui.GUICommands;

/**
 * This class is modeled from a wizard type dialog.
 * 
 * @see <a
 *      href="http://java.sun.com/developer/technicalArticles/GUI/swing/wizard/">Creating Wizard Dialogs with Java Swing</a> article.
 * 
 * @author Kyle O'Connor
 * 
 */
@SuppressWarnings("serial")
public class Wizard extends JPanel implements PropertyChangeListener {

	private final static Logger log = Logger.getLogger(Wizard.class);

	public static final String CANCEL_TEXT = "Cancel";
	public static final String NEXT_TEXT = "Next >";
	public static final String BACK_TEXT = "< Back";
	public static final String FINISH_TEXT = "Finish";

	private WizardModel wizardModel;
	private JPanel cardPanel;
	private CardLayout cardLayout;
	private JButton backButton;
	private JButton nextButton;
	private JButton cancelButton;

	/**
	 * Default constructor to create a <code>Wizard</code> which is a
	 * <code>JPanel</code>. This constructor sets up a default
	 * <code>WizardModel</code> and <code>PropertyChangeListener</code>.
	 */
	public Wizard() {
		wizardModel = new WizardModel();
		wizardModel.addPropertyChangeListener(this);
		initComponents();
	}

	private void initComponents() {
		this.setLayout(new BorderLayout());
		JPanel buttonPanel = new JPanel();
		JSeparator separator = new JSeparator();
		JSeparator topseparator = new JSeparator();
		Box buttonBox = new Box(BoxLayout.X_AXIS);

		cardPanel = new JPanel();
		cardPanel.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));

		cardLayout = new CardLayout();
		cardPanel.setLayout(cardLayout);

		backButton = new JButton(Wizard.BACK_TEXT);
		nextButton = new JButton(Wizard.NEXT_TEXT);
		cancelButton = new JButton(Wizard.CANCEL_TEXT);

		backButton.setActionCommand(GUICommands.BACK_BUTTON_ACTION_COMMAND);
		nextButton.setActionCommand(GUICommands.NEXT_BUTTON_ACTION_COMMAND);
		cancelButton.setActionCommand(GUICommands.CANCEL_BUTTON_ACTION_COMMAND);

		buttonPanel.setLayout(new BorderLayout());
		buttonPanel.add(separator, BorderLayout.NORTH);

		buttonBox.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
		buttonBox.add(backButton);
		buttonBox.add(Box.createHorizontalStrut(10));
		buttonBox.add(nextButton);
		buttonBox.add(Box.createHorizontalStrut(30));
		buttonBox.add(cancelButton);

		buttonPanel.add(buttonBox, BorderLayout.EAST);

		this.add(topseparator, BorderLayout.NORTH);
		this.add(buttonPanel, BorderLayout.SOUTH);
		this.add(cardPanel, BorderLayout.CENTER);
	}

	public void registerUIControl(ActionListener controller) {
		// Register action listeners for wizard buttons to MainController
		backButton.addActionListener(controller);
		nextButton.addActionListener(controller);
		cancelButton.addActionListener(controller);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seejava.beans.PropertyChangeListener#propertyChange(java.beans.
	 * PropertyChangeEvent)
	 */
	public void propertyChange(PropertyChangeEvent evt) {
		if (evt.getPropertyName().equals(WizardModel.CURRENT_PANEL_DESCRIPTOR_PROPERTY)) {
			resetButtonsToPanelRules();
		} else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_TEXT_PROPERTY)) {
			nextButton.setText(evt.getNewValue().toString());
		} else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_TEXT_PROPERTY)) {
			backButton.setText(evt.getNewValue().toString());
		} else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_TEXT_PROPERTY)) {
			cancelButton.setText(evt.getNewValue().toString());
		} else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_ENABLED_PROPERTY)) {
			nextButton.setEnabled(((Boolean) evt.getNewValue()).booleanValue());
		} else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_ENABLED_PROPERTY)) {
			backButton.setEnabled(((Boolean) evt.getNewValue()).booleanValue());
		} else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_ENABLED_PROPERTY)) {
			cancelButton.setEnabled(((Boolean) evt.getNewValue()).booleanValue());
		} else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_ICON_PROPERTY)) {
			nextButton.setIcon((Icon) evt.getNewValue());
		} else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_ICON_PROPERTY)) {
			backButton.setIcon((Icon) evt.getNewValue());
		} else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_ICON_PROPERTY)) {
			cancelButton.setIcon((Icon) evt.getNewValue());
		}
	}

	/**
	 * 
	 */
	private void resetButtonsToPanelRules() {
		// Reset the buttons to support the original panel rules,
		// including whether the next or back buttons are enabled or
		// disabled, or if the panel is finishable.

		WizardModel model = getModel();
		WizardPanelDescriptor descriptor = model.getCurrentPanelDescriptor();

		model.setCancelButtonText(Wizard.CANCEL_TEXT);

		// If the panel in question has another panel behind it, enable
		// the back button. Otherwise, disable it.

		model.setBackButtonText(Wizard.BACK_TEXT);

		if (descriptor.getBackPanelDescriptor() != null)
			model.setBackButtonEnabled(Boolean.TRUE);
		else
			model.setBackButtonEnabled(Boolean.FALSE);

		// If the panel in question has one or more panels in front of it,
		// enable the next button. Otherwise, disable it.

		if (descriptor.getNextPanelDescriptor() != null)
			model.setNextFinishButtonEnabled(Boolean.TRUE);
		else
			model.setNextFinishButtonEnabled(Boolean.FALSE);

		// If the panel in question is the last panel in the series, change
		// the Next button to Finish. Otherwise, set the text back to Next.

		if (descriptor.getNextPanelDescriptor() instanceof WizardPanelDescriptor.FinishIdentifier) {
			model.setNextFinishButtonText(Wizard.FINISH_TEXT);
		} else {
			model.setNextFinishButtonText(Wizard.NEXT_TEXT);
		}
	}

	public void setCurrentPanel(Object id) {
		if (id == null) {
			log.error("Error attempting to display panel with a null id");
			return;
		}

		WizardPanelDescriptor oldPanelDescriptor = wizardModel.getCurrentPanelDescriptor();
		if (oldPanelDescriptor != null)
			oldPanelDescriptor.aboutToHidePanel();

		wizardModel.setCurrentPanel(id);
		wizardModel.getCurrentPanelDescriptor().aboutToDisplayPanel();

		// Show the panel in the dialog.

		cardLayout.show(cardPanel, id.toString());
		wizardModel.getCurrentPanelDescriptor().displayingPanel();

	}

	public void registerWizardPanel(Object id, WizardPanelDescriptor panel) {
		// Add the incoming panel to our JPanel display that is managed by
		// the CardLayout layout manager.
		cardPanel.add(panel.getTargetPanel(), id);
		// Set a callback to the current wizard.
		panel.setWizard(this);
		// Place a reference to it in the model.
		wizardModel.registerPanel(id, panel);
	}

	public WizardModel getModel() {
		return wizardModel;
	}

	/**
	 * Mirrors the WizardModel method of the same name.
	 * 
	 * @return A boolean indicating if the button is enabled.
	 */
	public boolean getBackButtonEnabled() {
		return wizardModel.getBackButtonEnabled().booleanValue();
	}

	/**
	 * Mirrors the WizardModel method of the same name.
	 * 
	 * @param boolean newValue The new enabled status of the button.
	 */
	public void setBackButtonEnabled(boolean newValue) {
		wizardModel.setBackButtonEnabled(new Boolean(newValue));
	}

	/**
	 * Mirrors the WizardModel method of the same name.
	 * 
	 * @return A boolean indicating if the button is enabled.
	 */
	public boolean getNextFinishButtonEnabled() {
		return wizardModel.getNextFinishButtonEnabled().booleanValue();
	}

	/**
	 * Mirrors the WizardModel method of the same name.
	 * 
	 * @param boolean newValue The new enabled status of the button.
	 */
	public void setNextFinishButtonEnabled(boolean newValue) {
		wizardModel.setNextFinishButtonEnabled(new Boolean(newValue));
	}

	/**
	 * Mirrors the WizardModel method of the same name.
	 * 
	 * @return A boolean indicating if the button is enabled.
	 */
	public boolean getCancelButtonEnabled() {
		return wizardModel.getCancelButtonEnabled().booleanValue();
	}

	/**
	 * Mirrors the WizardModel method of the same name.
	 * 
	 * @param boolean newValue The new enabled status of the button.
	 */
	public void setCancelButtonEnabled(boolean newValue) {
		wizardModel.setCancelButtonEnabled(new Boolean(newValue));
	}

}
